"""
态势对象地理空间查询服务
"""
import re
import json
import math
from typing import List, Dict, Any, Tuple, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, text

from src.situation.models import SituationObject
from src.utils import logger


class GeoService:
    """地理空间查询服务"""

    @staticmethod
    def parse_coordinates_from_type_specific_data(type_specific_data: dict) -> Optional[Tuple[float, float]]:
        """
        从type_specific_data的JSON数据中解析经纬度坐标

        Args:
            type_specific_data: 类型特定数据JSON字典

        Returns:
            (经度, 纬度) 元组，如果解析失败返回 None
        """
        if not type_specific_data or not isinstance(type_specific_data, dict):
            return None

        try:
            # 优先从顶层latitude和longitude字段获取
            if "latitude" in type_specific_data and "longitude" in type_specific_data:
                lat = float(type_specific_data["latitude"])
                lon = float(type_specific_data["longitude"])
                if -90 <= lat <= 90 and -180 <= lon <= 180:
                    return (lon, lat)

            # 从coordinates字符串字段解析
            if "coordinates" in type_specific_data:
                coords_str = str(type_specific_data["coordinates"])
                # 匹配 "latitude, longitude" 格式
                pattern = r'(-?\d+\.?\d*)[,，]\s*(-?\d+\.?\d*)'
                match = re.search(pattern, coords_str)
                if match:
                    groups = match.groups()
                    if len(groups) >= 2:
                        lat = float(groups[0])  # 注意：coordinates字段中纬度在前
                        lon = float(groups[1])
                        if -90 <= lat <= 90 and -180 <= lon <= 180:
                            return (lon, lat)

            # 从original_data.coordinates中获取
            if "original_data" in type_specific_data and isinstance(type_specific_data["original_data"], dict):
                original_data = type_specific_data["original_data"]
                if "coordinates" in original_data and isinstance(original_data["coordinates"], dict):
                    coords = original_data["coordinates"]
                    if "latitude" in coords and "longitude" in coords:
                        lat = float(coords["latitude"])
                        lon = float(coords["longitude"])
                        if -90 <= lat <= 90 and -180 <= lon <= 180:
                            return (lon, lat)

        except (ValueError, TypeError, AttributeError) as e:
            logger.warning(f"解析type_specific_data坐标失败: {e}, 数据: {type_specific_data}")

        return None

    @staticmethod
    def parse_location(location_str: str) -> Optional[Tuple[float, float]]:
        """
        解析位置字符串，提取经纬度坐标（保留此方法以兼容旧数据）

        Args:
            location_str: 位置字符串，格式可能为 "经度,纬度" 或 "纬度,经度"

        Returns:
            (经度, 纬度) 元组，如果解析失败返回 None
        """
        if not location_str:
            return None

        # 匹配数字坐标模式，包括负数和小数
        # 格式可能是 "轨道位置: 125.67890，15.456789" 或 "125.67890,15.456789"
        pattern = r'(-?\d+\.?\d*)[,，]\s*(-?\d+\.?\d*)'
        match = re.search(pattern, location_str)

        if match:
            # 获取匹配的数字组
            groups = match.groups()
            if len(groups) >= 2:
                try:
                    lat = float(groups[1])
                    lon = float(groups[0])

                    # 验证坐标范围
                    if -90 <= lat <= 90 and -180 <= lon <= 180:
                        return (lon, lat)  # 返回 (经度, 纬度)
                except ValueError:
                    logger.warning(f"无法解析坐标: {location_str}")
                    return None

        return None

    @staticmethod
    def point_in_polygon(point: Tuple[float, float], polygon_vertices: List[Tuple[float, float]]) -> bool:
        """
        判断点是否在多边形内（射线算法）

        Args:
            point: 点坐标 (经度, 纬度)
            polygon_vertices: 多边形顶点列表 [(经度, 纬度), ...]

        Returns:
            True 如果点在多边形内，False 否则
        """
        if len(polygon_vertices) < 3:
            return False

        x, y = point
        n = len(polygon_vertices)
        inside = False

        p1x, p1y = polygon_vertices[0]
        for i in range(1, n + 1):
            p2x, p2y = polygon_vertices[i % n]
            if y > min(p1y, p2y):
                if y <= max(p1y, p2y):
                    if x <= max(p1x, p2x):
                        if p1y != p2y:
                            xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
                        if p1x == p2x or x <= xinters:
                            inside = not inside
            p1x, p1y = p2x, p2y

        return inside

    @staticmethod
    def create_polygon_from_positions(positions: List[List[float]]) -> Optional[List[Tuple[float, float]]]:
        """
        从positions数据创建多边形顶点列表

        Args:
            positions: 位置列表 [[经度, 纬度, 高度], ...]

        Returns:
            多边形顶点列表 [(经度, 纬度), ...]
        """
        if not positions or len(positions) < 3:
            return None

        vertices = []
        for pos in positions:
            if len(pos) >= 2:
                lon, lat = pos[0], pos[1]
                # 验证坐标范围
                if -180 <= lon <= 180 and -90 <= lat <= 90:
                    vertices.append((lon, lat))
                else:
                    logger.warning(f"无效坐标: ({lon}, {lat})")
                    return None

        return vertices if len(vertices) >= 3 else None

    @staticmethod
    async def get_objects_in_polygon(
        db: AsyncSession,
        polygon_vertices: List[Tuple[float, float]],
        object_types: Optional[List[str]] = None
    ) -> Dict[str, List[Dict[str, Any]]]:
        """
        查询多边形范围内的态势对象

        Args:
            db: 数据库会话
            polygon_vertices: 多边形顶点列表 [(经度, 纬度), ...]
            object_types: 可选的对象类型筛选列表

        Returns:
            按类型分组的对象字典
        """
        if not polygon_vertices or len(polygon_vertices) < 3:
            return {
                "land_targets": [],
                "sea_targets": [],
                "infrastructure": [],
                "observation_equipment": []
            }

        # 构建基础查询条件
        conditions = [
            SituationObject.is_deleted == False,
            SituationObject.location.isnot(None),
            SituationObject.location != ""
        ]

        # 添加对象类型筛选
        if object_types:
            conditions.append(SituationObject.object_type.in_(object_types))
        else:
            # 默认查询陆上目标、海上目标和基础设施
            conditions.append(
                SituationObject.object_type.in_(['land_target', 'sea_target', 'infrastructure'])
            )

        try:
            # 查询所有可能符合条件的对象
            stmt = select(SituationObject).where(and_(*conditions))
            result = await db.execute(stmt)
            objects = result.scalars().all()

            # 按类型分组结果
            categorized_objects = {
                "land_targets": [],
                "sea_targets": [],
                "infrastructure": [],
                "observation_equipment": []
            }

            for obj in objects:
                # 优先从type_specific_data解析对象位置
                coords = GeoService.parse_coordinates_from_type_specific_data(obj.type_specific_data)

                # 如果type_specific_data中没有坐标，尝试从location字段解析（兼容旧数据）
                if not coords:
                    coords = GeoService.parse_location(obj.location)

                if coords:
                    # 检查点是否在多边形内
                    if GeoService.point_in_polygon(coords, polygon_vertices):
                        obj_data = {
                            "id": str(obj.id),
                            "name": obj.name,
                            "code": obj.code,
                            "object_type": obj.object_type,
                            "equipment_category": obj.equipment_category,
                            "location": obj.location,
                            "coordinates": {
                                "longitude": coords[0],
                                "latitude": coords[1]
                            },
                            "organization": obj.organization,
                            "deployment_status": obj.deployment_status,
                            "description": obj.description,
                            "created_at": obj.created_at.isoformat() if obj.created_at else None
                        }

                        # 按对象类型分类
                        if obj.object_type == 'land_target':
                            categorized_objects["land_targets"].append(obj_data)
                        elif obj.object_type == 'sea_target':
                            categorized_objects["sea_targets"].append(obj_data)
                        elif obj.object_type == 'infrastructure':
                            categorized_objects["infrastructure"].append(obj_data)
                        elif obj.object_type == 'observation_equipment':
                            categorized_objects["observation_equipment"].append(obj_data)

            logger.info(f"多边形查询完成: 找到 {sum(len(v) for v in categorized_objects.values())} 个对象")
            return categorized_objects

        except Exception as e:
            logger.error(f"地理空间查询失败: {e}")
            return {
                "land_targets": [],
                "sea_targets": [],
                "infrastructure": [],
                "observation_equipment": []
            }

    @staticmethod
    def calculate_polygon_area(positions: List[List[float]]) -> float:
        """
        计算多边形面积（平方公里）
        使用鞋带公式（Shoelace formula）计算多边形面积

        Args:
            positions: 位置列表 [[经度, 纬度, 高度], ...]

        Returns:
            面积（平方公里）
        """
        vertices = GeoService.create_polygon_from_positions(positions)
        if not vertices or len(vertices) < 3:
            return 0.0

        try:
            # 使用鞋带公式计算多边形面积
            area = 0.0
            n = len(vertices)

            for i in range(n):
                j = (i + 1) % n
                lat1, lon1 = vertices[i][1], vertices[i][0]
                lat2, lon2 = vertices[j][1], vertices[j][0]
                area += (lon2 - lon1) * (lat2 + lat1)

            # 面积的绝对值除以2
            area = abs(area) / 2.0

            # 转换度数到公里（粗略计算，在赤道附近）
            # 1度纬度 ≈ 111公里
            # 1度经度 ≈ 111公里 * cos(平均纬度)
            avg_lat = sum(v[1] for v in vertices) / n
            lat_km = 111.0
            lon_km = 111.0 * math.cos(math.radians(avg_lat))

            area_km2 = area * lat_km * lon_km

            return round(area_km2, 2)

        except Exception as e:
            logger.error(f"计算多边形面积失败: {e}")
            return 0.0